September 10, 2021
이번 시간에는 Kaggle 입문자들이 Kaggle을 시작할 때 많이 해보는 Digit-Recognizer에 대해 이야기 해보려고 합니다!😊 많은 사람들이 Kaggle을 시작할 때 이 competition에 참가해보고 많은 notebook들을 보면서 딥러닝이나 머신러닝이 무엇이고 어떻게 해야하는지 알게되는데요! Digit-Recognizer은 MNIST data를 분류해보는 입문자용 competition입니다. 이 대회를 하면서 구현 관점에서 알게되거나 느낀점을 바탕으로 해당 포스트를 작성하고자 합니다. 아래는 이번 포스팅의 Category입니다!
① version_1
사용한 Architecture는 다음과 같습니다.
4conv- batch - relu , 1pooling, 3fc layers
또한, He initilizationm과 batch normalization을 적용하여 구현을 하였습니다. 4개의 convolution layer와 batchnorm, relu를 적용하였고 1번의 pooling을 사용했습니다. 또한 3개의 fc layer를 사용하고 2개의 dropout을 적용했습니다. 또한 early-stopping 기법을 사용했습니다.
또한 early stopping을 사용하여 model이 training data에 overfitting되는 것을 막았습니다.
#conv1->batch1->relu->conv2->batch2->relu->pool->conv3->batch3->relu->conv4->batch4->relu
#->fc1->relu->drop->fc2->relu->drop->fc3
class CNN(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(in_channels=1, out_channels=16, kernel_size=3, stride=1)
self.conv2 = nn.Conv2d(in_channels=16, out_channels=32, kernel_size=3, stride=1)
self.conv3 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, stride=1)
self.conv4 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1)
self.fc1 = nn.Linear(128*8*8,1024)
self.fc2 = nn.Linear(1024,512)
self.fc3 = nn.Linear(512,10)
self.batchnorm1 = nn.BatchNorm2d(16)
self.batchnorm2 = nn.BatchNorm2d(32)
self.batchnorm3 = nn.BatchNorm2d(64)
self.batchnorm4 = nn.BatchNorm2d(128)
self.drop = nn.Dropout2d(p=0.5)
self.max_pool = nn.MaxPool2d(kernel_size=2, stride=2)
self.relu = nn.ReLU()
#initilization
nn.init.kaiming_normal_(self.conv1.weight, mode='fan_in', nonlinearity='relu')
nn.init.kaiming_normal_(self.conv2.weight, mode='fan_in', nonlinearity='relu')
nn.init.kaiming_normal_(self.conv3.weight, mode='fan_in', nonlinearity='relu')
nn.init.kaiming_normal_(self.conv4.weight, mode='fan_in', nonlinearity='relu')
nn.init.kaiming_normal_(self.fc1.weight, mode='fan_in', nonlinearity='relu')
nn.init.kaiming_normal_(self.fc2.weight, mode='fan_in', nonlinearity='relu')
nn.init.kaiming_normal_(self.fc3.weight, mode='fan_in', nonlinearity='relu')
def forward(self,x):
#x shape: 64*1*28*28
out = self.conv1(x)
out = self.batchnorm1(out)
out = self.relu(out)
#64*16*26*26
out = self.conv2(out)
out = self.batchnorm2(out)
out = self.relu(out)
#64*32*24*24
out = self.max_pool(out)
#64*32*12*12
out = self.conv3(out)
out = self.batchnorm3(out)
out = self.relu(out)
#64*64*10*10
out = self.conv4(out)
out = self.batchnorm4(out)
out = self.relu(out)
#64*128*8*8
#flatten
out = out.view(out.size(0),-1)
#64*128*8*8
out = self.fc1(out)
out = self.relu(out)
out = self.drop(out)
out = self.fc2(out)
out = self.relu(out)
out = self.drop(out)
out = self.fc3(out)① version_2
transforms.ToPILImage()를 사용하는 이유는 csv파일 형태로 데이터셋을 받을 경우, 바로 torch.Tensor로 바꿔줄 수 없기 때문입니다. transforms.ToTensor()는 numpy.ndarray나 PIL image를 pytorch tensor로 바꿔주기 때문에 csv에서 받은 dataframe을 PIL image로 바꿔주고 이 PIL image를 pytorch tensor로 바꿔주어야합니다. 이에 아래와 같이 작성합니다.
from torchvision import transforms
transfrom = transforms.compose([
transforms.ToPILImage(),
transforms.ToTensor()
])data를 어떻게 transform시킬지는 custom dataset을 초기화시킬 때, 인자로 넣어주는 것을 흔하게 볼 수 있었습니다. 아래의 그림처럼 말이죠!😎
class MNIST_data(Dataset):
def __init__(self,
file_path,
transform = transforms.Compose([transforms.ToPILImage(),
transforms.ToTensor(),
transforms.Normalize(mean=(0.5,), std=(0.5,))])
):np.arange(1,len(test_data)+1)을 통해 쉽게 csv의 column을 만들 수 있었습니다~!torch→numpy로 변환할 땐 . numpy()를, Dataframe→numpy로 변환할 땐 .values 대신에 .to_numpy()를 사용합니다. .values는 deprecate(비추천!)한다고 하더라구요!
또한, numpy를 torch.Tensor로 바꾸기 위해선 torch.from_numpy()를 사용합니다. 이 경우, shape는 유지한 채로 type만 변환됩니다!
matplolib이나 cv2의 경우, numpy.ndarray를 많이 사용합니다. 이 때, numpy.ndarray들의 format은 HWC인데 torch.Tensor의 경우 CHW의 format을 갖습니다. 따라서 HWC인 numpy.ndarray들을 CHW format을 갖고 있는 troch.Tensor로 바꿔주기 위해서 torchvision.transfroms.ToTensor()를 사용합니다. 따라서, shape를 바꿔주기 위해선 6.의 torch.from_numpy()를 사용하는 것이아니라 ToTensor()를 사용해야합니다.
또한 아래와 같은 방법으로 torchvision.transfroms.ToTensor()을 사용할 수 있습니다.
plt.scatter()을 사용하면 점으로, plt.plot을 사용하면 그래프로 그려집니다!cv2.imread는 이미지를 받아 numpy.ndarray로 반환합니다. 따라서 csv파일을 읽을 때, cv2로 image를 읽은 듯한 효과를 주기 위해선 pandas의 read_csv를 사용하여 DataFrame으로 이미지를 읽은 후 to_numpy()를 통해 numpy.ndarray로 바꿔주면 됩니다. 즉, 평상시에 이미지를 읽을 때 numpy.ndarray로 읽으므로 DataFrame을 받게되면 이것 또한 numpy.ndarray로 바꿔서 data augmentation(albumentation을 통해)을 수행하면 됩니다. albumentation을 사용할 경우, numpy.ndarraycv2.imread를 통해 image를 numpy.ndarray로 읽어 data augmentation을 수행합니다. cv2.imread를 통해 읽게 되면 HWC format을 가진 numpy.ndarray를 받게 되는데 Albumentation은 HWC format을 가진 numpy.ndarray를 그대로 받아 data augmentation을 수행합니다. 또한 matplotlib.pyplot.imshow()는 argument로 numpy.ndarray를 받는데 이 떄의 format 또한 HWC format이 됩니다.
즉, Albumentations, matplotlib, cv2는 HWC format을 가지는 numpy.ndarray을 다룹니다. 따라서 torch.Tensor에서의 이미지는 CHW format이므로 순서를 바꿔줘야합니다. 이에torchvision.transfroms.ToTensor()을 사용해야한다고 말했었죠?!😉np.repeat(ndarray, 반복 횟수, axis=)을 통해 image차원을 늘려줄 수 있습니다. MNIST 데이터의 경우 1차원일 땐 이상한 색이 나오게 되지만 image = np.repeat(image,3,axis=2)을 사용해서 3차원으로 늘리게 되면 익숙한 하얀색 숫자가 나오게 됩니다. 아래의 그림들 처럼 출력되게 됩니다.
import Albumentations.pytorch.transforms.ToTensor()를 통해 pytorch tensor로 변환가능합니다.transforms.ToPILImage()를 왜 사용하는가?: https://green-late7.tistory.com/56DataFrame.to_numpy(): https://stackoverflow.com/questions/13187778/convert-pandas-dataframe-to-numpy-array/plt.subplots(nrows=,ncols=,figsize=)사용법: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.subplots.htmlnp.repeat(): https://numpy.org/doc/stable/reference/generated/numpy.repeat.html